/*******************************************************************************/
/*                                                                             */
/*  Copyright (c) 2009: Peter Schregle,                                        */
/*  All rights reserved.                                                       */
/*                                                                             */
/*  This file is part of the Property Library.                                 */
/*                                                                             */
/*  Redistribution of the Property Library and use in source and binary forms, */
/*  with or without modification, are permitted provided that the following    */
/*  conditions are met:                                                        */
/*  1. Redistributions of source code must retain the above copyright notice,  */
/*     this list of conditions and the following disclaimer.                   */
/*  2. Redistributions in binary form must reproduce the above copyright       */
/*     notice, this list of conditions and the following disclaimer in the     */
/*     documentation and/or other materials provided with the distribution.    */
/*  3. Neither the name of Peter Schregle nor the names of other contributors  */
/*     may be used to endorse or promote products derived from this software   */
/*     without specific prior written permission.                              */
/*                                                                             */
/*  THIS SOFTWARE IS PROVIDED BY PETER SCHREGLE AND CONTRIBUTORS 'AS IS' AND   */
/*  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE      */
/*  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE */
/*  ARE DISCLAIMED. IN NO EVENT SHALL PETER SCHREGLE OR CONTRIBUTORS BE LIABLE */
/*  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL */
/*  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS    */
/*  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)      */
/*  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,        */
/*  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN   */
/*  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE            */
/*  POSSIBILITY OF SUCH DAMAGE.                                                */
/*                                                                             */
/*******************************************************************************/


#include <boost/test/unit_test.hpp>

#include "../../include/pl/property.h"
#include <boost/static_assert.hpp>


BOOST_AUTO_TEST_SUITE( property_suite )

struct rwfe_member_select_1
{
	pl::property_rwfe<int, void> u;
	pl::property_rwfe<int, void> v;

	// Make sure that the pl::property_rwfe is space-optimal.
	BOOST_STATIC_ASSERT(sizeof(int) + sizeof(void*) == 
		sizeof(pl::property_rwfe<int, void>));
};

struct rwfe_member_select_2
{
	pl::property_rwfe<rwfe_member_select_1, void> x;
	pl::property_rwfe<rwfe_member_select_1, void> y;

	// Make sure that the pl::property_rwfe is space-optimal.
	BOOST_STATIC_ASSERT(sizeof(rwfe_member_select_1) + sizeof(void*) == 
		sizeof(pl::property_rwfe<rwfe_member_select_1, void>));
};

BOOST_AUTO_TEST_CASE( property_rwfe_default_construct )
{
	pl::property_rwfe<int, void> a;
	BOOST_CHECK( a == 0 );
}

BOOST_AUTO_TEST_CASE( property_rwfe_construct )
{
	pl::property_rwfe<int, void> a(1);
	BOOST_CHECK( a == 1 );

	pl::property_rwfe<int, void> b = 1;
	BOOST_CHECK( b == 1 );
}

BOOST_AUTO_TEST_CASE( property_rwfe_property_assign )
{
	pl::property_rwfe<int, void> a;
	BOOST_CHECK( a == 0 );
	pl::property_rwfe<int, void> b(1);
	BOOST_CHECK( b == 1 );
	a = b;
	BOOST_CHECK( a == 1 );
}

BOOST_AUTO_TEST_CASE( property_rwfe_assign )
{
	pl::property_rwfe<int, void> a;
	BOOST_CHECK( a == 0 );
	a = 1;
	BOOST_CHECK( a == 1 );
}

BOOST_AUTO_TEST_CASE( property_rwfe_member_select_dot )
{
	rwfe_member_select_2 a;
	BOOST_CHECK( a.x->u == 0 );
	a.x->u = 1;
	BOOST_CHECK( a.x->u == 1 );
	BOOST_CHECK( a.x->v == 0 );
	a.x->v = 2;
	BOOST_CHECK( a.x->v == 2 );
	BOOST_CHECK( a.y->u == 0 );
	a.y->u = 3;
	BOOST_CHECK( a.y->u == 3 );
	BOOST_CHECK( a.y->v == 0 );
	a.y->v = 4;
	BOOST_CHECK( a.y->v == 4 );
}

BOOST_AUTO_TEST_CASE( property_rwfe_add_assign )
{
	pl::property_rwfe<int, void> a;
	for (int i=0; i<=1000; i+=10)
	{
		BOOST_CHECK( a == i );
		a += 10;
		BOOST_CHECK( a == i+10 );
	}
}

BOOST_AUTO_TEST_CASE( property_rwfe_add )
{
	pl::property_rwfe<int, void> a;
	for (int i=0; i<=1000; i+=10)
	{
		BOOST_CHECK( a == i );
		a = a + 10;
		BOOST_CHECK( a == i+10 );
	}
}

BOOST_AUTO_TEST_CASE( property_rwfe_subtract_assign )
{
	pl::property_rwfe<int, void> a(1000);
	for (int i=1000; i>=0; i-=10)
	{
		BOOST_CHECK( a == i );
		a -= 10;
		BOOST_CHECK( a == i-10 );
	}
}

BOOST_AUTO_TEST_CASE( property_rwfe_subtract )
{
	pl::property_rwfe<int, void> a(1000);
	for (int i=1000; i>=0; i-=10)
	{
		BOOST_CHECK( a == i );
		a = a - 10;
		BOOST_CHECK( a == i-10 );
	}
}

BOOST_AUTO_TEST_CASE( property_rwfe_multiply_assign )
{
	pl::property_rwfe<int, void> a(1);
	BOOST_CHECK( a == 1 );
	a *= 10;
	BOOST_CHECK( a == 10 );
	a *= 10;
	BOOST_CHECK( a == 100 );
	a *= 10;
	BOOST_CHECK( a == 1000 );
	a *= 10;
	BOOST_CHECK( a == 10000 );
	a *= 10;
	BOOST_CHECK( a == 100000 );
}

BOOST_AUTO_TEST_CASE( property_rwfe_multiply )
{
	pl::property_rwfe<int, void> a(1);
	BOOST_CHECK( a == 1 );
	a = a* 10;
	BOOST_CHECK( a == 10 );
	a = a* 10;
	BOOST_CHECK( a == 100 );
	a = a* 10;
	BOOST_CHECK( a == 1000 );
	a = a* 10;
	BOOST_CHECK( a == 10000 );
	a = a* 10;
	BOOST_CHECK( a == 100000 );
}

BOOST_AUTO_TEST_CASE( property_rwfe_divide_assign )
{
	pl::property_rwfe<int, void> a(100000);
	BOOST_CHECK( a == 100000 );
	a /= 10;
	BOOST_CHECK( a == 10000 );
	a /= 10;
	BOOST_CHECK( a == 1000 );
	a /= 10;
	BOOST_CHECK( a == 100 );
	a /= 10;
	BOOST_CHECK( a == 10 );
	a /= 10;
	BOOST_CHECK( a == 1 );
}

BOOST_AUTO_TEST_CASE( property_rwfe_divide )
{
	pl::property_rwfe<int, void> a(100000);
	BOOST_CHECK( a == 100000 );
	a = a / 10;
	BOOST_CHECK( a == 10000 );
	a = a / 10;
	BOOST_CHECK( a == 1000 );
	a = a / 10;
	BOOST_CHECK( a == 100 );
	a = a / 10;
	BOOST_CHECK( a == 10 );
	a = a / 10;
	BOOST_CHECK( a == 1 );
}

BOOST_AUTO_TEST_CASE( property_rwfe_less_than )
{
	pl::property_rwfe<int, void> a(0);
	pl::property_rwfe<int, void> b(1);
	BOOST_CHECK( a < b );
	BOOST_CHECK( !(b < a) );
}

BOOST_AUTO_TEST_CASE( property_rwfe_less_than_or_equal )
{
	pl::property_rwfe<int, void> a(0);
	pl::property_rwfe<int, void> b(1);
	BOOST_CHECK( a <= b );
	BOOST_CHECK( !(b <= a) );
	pl::property_rwfe<int, void> c(0);
	BOOST_CHECK( a <= c );
	BOOST_CHECK( c <= a );
}

BOOST_AUTO_TEST_CASE( property_rwfe_greater_than )
{
	pl::property_rwfe<int, void> a(0);
	pl::property_rwfe<int, void> b(1);
	BOOST_CHECK( b > a );
	BOOST_CHECK( !(a > b) );
}

BOOST_AUTO_TEST_CASE( property_rwfe_greater_than_or_equal )
{
	pl::property_rwfe<int, void> a(0);
	pl::property_rwfe<int, void> b(1);
	BOOST_CHECK( b >= a );
	BOOST_CHECK( !(a >= b) );
	pl::property_rwfe<int, void> c(0);
	BOOST_CHECK( a >= c );
	BOOST_CHECK( c >= a );
}

BOOST_AUTO_TEST_CASE( property_rwfe_equal )
{
	pl::property_rwfe<int, void> a(0);
	pl::property_rwfe<int, void> b(0);
	BOOST_CHECK( a == b );
	BOOST_CHECK( b == a );
	pl::property_rwfe<int, void> c(1);
	BOOST_CHECK( !(a == c) );
	BOOST_CHECK( !(c == a) );
}

BOOST_AUTO_TEST_CASE( property_rwfe_not_equal )
{
	pl::property_rwfe<int, void> a(0);
	pl::property_rwfe<int, void> b(1);
	BOOST_CHECK( a != b );
	BOOST_CHECK( b != a );
	pl::property_rwfe<int, void> c(0);
	BOOST_CHECK( !(a != c) );
	BOOST_CHECK( !(c != a) );
}

BOOST_AUTO_TEST_CASE( property_rwfe_not )
{
	pl::property_rwfe<int, void> a(0);
	BOOST_CHECK( !a );

	pl::property_rwfe<int, void> b(1);
	BOOST_CHECK( !(!b) );

	pl::property_rwfe<int, void> c(1000);
	BOOST_CHECK( !(!c) );
}

BOOST_AUTO_TEST_CASE( property_rwfe_unary_minus )
{
	pl::property_rwfe<int, void> a(0);
	BOOST_CHECK( a == -a );
	BOOST_CHECK( -a == a );

	pl::property_rwfe<int, void> b(1);
	pl::property_rwfe<int, void> c(-1);
	BOOST_CHECK( b == -c );
	BOOST_CHECK( -c == b );
	BOOST_CHECK( -b == c );
	BOOST_CHECK( c == -b );
}

BOOST_AUTO_TEST_CASE( property_rwfe_pre_increment )
{
	pl::property_rwfe<int, void> a;
	for (int i=0; i<=1000; ++i)
	{
		BOOST_CHECK( a == i );
		BOOST_CHECK( ++a == i+1 );
	}
}

BOOST_AUTO_TEST_CASE( property_rwfe_post_increment )
{
	pl::property_rwfe<int, void> a;
	for (int i=0; i<=1000; ++i)
	{
		BOOST_CHECK( a == i );
		BOOST_CHECK( a++ == i );
		BOOST_CHECK( a == i+1 );
	}
}

BOOST_AUTO_TEST_CASE( property_rwfe_pre_decrement )
{
	pl::property_rwfe<int, void> a(1000);
	for (int i=1000; i>=0; --i)
	{
		BOOST_CHECK( a == i );
		BOOST_CHECK( --a == i-1 );
	}
}

BOOST_AUTO_TEST_CASE( property_rwfe_post_decrement )
{
	pl::property_rwfe<int, void> a(1000);
	for (int i=1000; i>=0; --i)
	{
		BOOST_CHECK( a == i );
		BOOST_CHECK( a-- == i );
		BOOST_CHECK( a == i-1 );
	}
}

BOOST_AUTO_TEST_CASE( property_rwfe_shift_right_assign )
{
	pl::property_rwfe<int, void> a(16777216);
	BOOST_CHECK( a == 16777216 );
	a >>= 1;
	BOOST_CHECK( a == 8388608 );
	a >>= 1;
	BOOST_CHECK( a == 4194304 );
	a >>= 1;
	BOOST_CHECK( a == 2097152 );
	a >>= 1;
	BOOST_CHECK( a == 1048576 );
	a >>= 1;
	BOOST_CHECK( a == 524288 );
	a >>= 1;
	BOOST_CHECK( a == 262144 );
	a >>= 1;
	BOOST_CHECK( a == 131072 );
	a >>= 1;
	BOOST_CHECK( a == 65536 );
	a >>= 1;
	BOOST_CHECK( a == 32768 );
	a >>= 1;
	BOOST_CHECK( a == 16384 );
	a >>= 1;
	BOOST_CHECK( a == 8192 );
	a >>= 1;
	BOOST_CHECK( a == 4096 );
	a >>= 1;
	BOOST_CHECK( a == 2048 );
	a >>= 1;
	BOOST_CHECK( a == 1024 );
	a >>= 1;
	BOOST_CHECK( a == 512 );
	a >>= 1;
	BOOST_CHECK( a == 256 );
	a >>= 1;
	BOOST_CHECK( a == 128 );
	a >>= 1;
	BOOST_CHECK( a == 64 );
	a >>= 1;
	BOOST_CHECK( a == 32 );
	a >>= 1;
	BOOST_CHECK( a == 16 );
	a >>= 1;
	BOOST_CHECK( a == 8 );
	a >>= 1;
	BOOST_CHECK( a == 4 );
	a >>= 1;
	BOOST_CHECK( a == 2 );
	a >>= 1;
	BOOST_CHECK( a == 1 );
	a >>= 1;
	BOOST_CHECK( a == 0 );
}

BOOST_AUTO_TEST_CASE( property_rwfe_shift_right )
{
	pl::property_rwfe<int, void> a(16777216);
	BOOST_CHECK( a == 16777216 );
	a = a >> 1;
	BOOST_CHECK( a == 8388608 );
	a = a >> 1;
	BOOST_CHECK( a == 4194304 );
	a = a >> 1;
	BOOST_CHECK( a == 2097152 );
	a = a >> 1;
	BOOST_CHECK( a == 1048576 );
	a = a >> 1;
	BOOST_CHECK( a == 524288 );
	a = a >> 1;
	BOOST_CHECK( a == 262144 );
	a = a >> 1;
	BOOST_CHECK( a == 131072 );
	a = a >> 1;
	BOOST_CHECK( a == 65536 );
	a = a >> 1;
	BOOST_CHECK( a == 32768 );
	a = a >> 1;
	BOOST_CHECK( a == 16384 );
	a = a >> 1;
	BOOST_CHECK( a == 8192 );
	a = a >> 1;
	BOOST_CHECK( a == 4096 );
	a = a >> 1;
	BOOST_CHECK( a == 2048 );
	a = a >> 1;
	BOOST_CHECK( a == 1024 );
	a = a >> 1;
	BOOST_CHECK( a == 512 );
	a = a >> 1;
	BOOST_CHECK( a == 256 );
	a = a >> 1;
	BOOST_CHECK( a == 128 );
	a = a >> 1;
	BOOST_CHECK( a == 64 );
	a = a >> 1;
	BOOST_CHECK( a == 32 );
	a = a >> 1;
	BOOST_CHECK( a == 16 );
	a = a >> 1;
	BOOST_CHECK( a == 8 );
	a = a >> 1;
	BOOST_CHECK( a == 4 );
	a = a >> 1;
	BOOST_CHECK( a == 2 );
	a = a >> 1;
	BOOST_CHECK( a == 1 );
	a = a >> 1;
	BOOST_CHECK( a == 0 );
}

BOOST_AUTO_TEST_CASE( property_rwfe_shift_left_assign )
{
	pl::property_rwfe<int, void> a(1);
	BOOST_CHECK( a == 1 );
	a <<= 1;
	BOOST_CHECK( a == 2 );
	a <<= 1;
	BOOST_CHECK( a == 4 );
	a <<= 1;
	BOOST_CHECK( a == 8 );
	a <<= 1;
	BOOST_CHECK( a == 16 );
	a <<= 1;
	BOOST_CHECK( a == 32 );
	a <<= 1;
	BOOST_CHECK( a == 64 );
	a <<= 1;
	BOOST_CHECK( a == 128 );
	a <<= 1;
	BOOST_CHECK( a == 256 );
	a <<= 1;
	BOOST_CHECK( a == 512 );
	a <<= 1;
	BOOST_CHECK( a == 1024 );
	a <<= 1;
	BOOST_CHECK( a == 2048 );
	a <<= 1;
	BOOST_CHECK( a == 4096 );
	a <<= 1;
	BOOST_CHECK( a == 8192 );
	a <<= 1;
	BOOST_CHECK( a == 16384 );
	a <<= 1;
	BOOST_CHECK( a == 32768 );
	a <<= 1;
	BOOST_CHECK( a == 65536 );
	a <<= 1;
	BOOST_CHECK( a == 131072 );
	a <<= 1;
	BOOST_CHECK( a == 262144 );
	a <<= 1;
	BOOST_CHECK( a == 524288 );
	a <<= 1;
	BOOST_CHECK( a == 1048576 );
	a <<= 1;
	BOOST_CHECK( a == 2097152 );
	a <<= 1;
	BOOST_CHECK( a == 4194304 );
	a <<= 1;
	BOOST_CHECK( a == 8388608 );
	a <<= 1;
	BOOST_CHECK( a == 16777216 );
}

BOOST_AUTO_TEST_CASE( property_rwfe_shift_left )
{
	pl::property_rwfe<int, void> a(1);
	BOOST_CHECK( a == 1 );
	a = a << 1;
	BOOST_CHECK( a == 2 );
	a = a << 1;
	BOOST_CHECK( a == 4 );
	a = a << 1;
	BOOST_CHECK( a == 8 );
	a = a << 1;
	BOOST_CHECK( a == 16 );
	a = a << 1;
	BOOST_CHECK( a == 32 );
	a = a << 1;
	BOOST_CHECK( a == 64 );
	a = a << 1;
	BOOST_CHECK( a == 128 );
	a = a << 1;
	BOOST_CHECK( a == 256 );
	a = a << 1;
	BOOST_CHECK( a == 512 );
	a = a << 1;
	BOOST_CHECK( a == 1024 );
	a = a << 1;
	BOOST_CHECK( a == 2048 );
	a = a << 1;
	BOOST_CHECK( a == 4096 );
	a = a << 1;
	BOOST_CHECK( a == 8192 );
	a = a << 1;
	BOOST_CHECK( a == 16384 );
	a <<= 1;
	BOOST_CHECK( a == 32768 );
	a <<= 1;
	BOOST_CHECK( a == 65536 );
	a = a << 1;
	BOOST_CHECK( a == 131072 );
	a = a << 1;
	BOOST_CHECK( a == 262144 );
	a = a << 1;
	BOOST_CHECK( a == 524288 );
	a = a << 1;
	BOOST_CHECK( a == 1048576 );
	a = a << 1;
	BOOST_CHECK( a == 2097152 );
	a = a << 1;
	BOOST_CHECK( a == 4194304 );
	a = a << 1;
	BOOST_CHECK( a == 8388608 );
	a = a << 1;
	BOOST_CHECK( a == 16777216 );
}

int last_value_on_get = -1;

void my_on_get(void const*, int const& value)
{
	last_value_on_get = value;
}

BOOST_AUTO_TEST_CASE( property_rwfe_on_get )
{
	pl::property_rwfe<int, void> a;
	a.on_get = my_on_get;
	BOOST_CHECK( last_value_on_get != 0 );
	BOOST_CHECK( a == 0 );
	BOOST_CHECK( last_value_on_get == 0 );
	a = 1;
	BOOST_CHECK( last_value_on_get != 1 );
	BOOST_CHECK( a == 1 );
	BOOST_CHECK( last_value_on_get == 1 );

	a.on_get = 0;
}

int last_value_on_set = -1;

void my_on_set(void const*, int & value)
{
	last_value_on_set = value;
}

void my_on_set_2(void const*, int & value)
{
	value += 1;
}

BOOST_AUTO_TEST_CASE( property_rwfe_on_set )
{
	pl::property_rwfe<int, void> a;
	a.on_set = my_on_set;
	BOOST_CHECK( a == 0 );
	BOOST_CHECK( last_value_on_set != 0 );
	a = 1;
	BOOST_CHECK( a == 1 );
	BOOST_CHECK( last_value_on_set == 1 );

	a.on_set = my_on_set_2;
	a = 1;
	BOOST_CHECK( a == 2 );

	a.on_set = 0;
}

int last_value_current_on_changing = -1;
int last_value_new_on_changing = -1;

void my_on_changing(void const*, int const& current_value, int const& new_value)
{
	last_value_current_on_changing = current_value;
	last_value_new_on_changing = new_value;
}

BOOST_AUTO_TEST_CASE( property_rwfe_on_changing )
{
	pl::property_rwfe<int, void> a;
	a.on_changing = my_on_changing;
	BOOST_CHECK( a == 0 );
	BOOST_CHECK( last_value_current_on_changing == -1 );
	BOOST_CHECK( last_value_new_on_changing == -1 );
	a = 0;
	BOOST_CHECK( a == 0 );
	BOOST_CHECK( last_value_current_on_changing == -1 );
	BOOST_CHECK( last_value_new_on_changing == -1 );
	a = 1;
	BOOST_CHECK( a == 1 );
	BOOST_CHECK( last_value_current_on_changing == 0 );
	BOOST_CHECK( last_value_new_on_changing == 1 );
	a = 0;
	BOOST_CHECK( a == 0 );
	BOOST_CHECK( last_value_current_on_changing == 1 );
	BOOST_CHECK( last_value_new_on_changing == 0 );

	a.on_changing = 0;
}

int last_value_on_changed = -1;

void my_on_changed(void const*, int const& value)
{
	last_value_on_changed = value;
}

BOOST_AUTO_TEST_CASE( property_rwfe_on_changed )
{
	pl::property_rwfe<int, void> a;
	a.on_changed = my_on_changed;
	BOOST_CHECK( a == 0 );
	BOOST_CHECK( last_value_on_changed == -1 );
	a = 0;
	BOOST_CHECK( a == 0 );
	BOOST_CHECK( last_value_on_changed == -1 );
	a = 1;
	BOOST_CHECK( a == 1 );
	BOOST_CHECK( last_value_on_changed == 1 );
	a = 0;
	BOOST_CHECK( a == 0 );
	BOOST_CHECK( last_value_on_changed == 0 );

	a.on_changed = 0;
}


BOOST_AUTO_TEST_SUITE_END()

